#include <windows.h>
#include <stdio.h>
#include "helpers.h"


// ----------------------------------------------

// launches AIMP or WinAmp. returns winamp's window handle (or NULL on failure).
HWND StartPlayer()
{
	const int stringSize=1024;
	WCHAR winampLocation[stringSize], aimpLocation[stringSize], *playerLocation;

	GetWinampAndAimpLocations(winampLocation, aimpLocation, stringSize);
	if(aimpLocation[0]!=0)
		playerLocation=aimpLocation;	// use AIMP even if both players found
	else if(winampLocation[0]!=0)
		playerLocation=winampLocation;
	else
		return NULL;	// players not found

	// start player
	STARTUPINFOW si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	si.cb=sizeof(si);
	CreateProcessW(NULL, playerLocation, NULL, NULL, false, 0, NULL, NULL, &si, &pi);
	// waiting for winamp initialization is complete
	DWORD dwres=WaitForInputIdle(pi.hProcess, 20000);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	if(dwres!=0) return NULL;	// some error

	// wait for player's main window
	HWND hwndWinamp;
	if(!(hwndWinamp=WaitForWindow(L"Winamp v1.x", NULL, 5000))) return NULL;	// timeout
	SendMessage(hwndWinamp, WM_NULL, 0, 0);	// wait for window response
	//Beep(5000, 100);
	//Sleep(1000);

	return hwndWinamp;
}

// ----------------------------------------------

void GetWinampAndAimpLocations(LPWSTR winampLocation, LPWSTR aimpLocation, DWORD stringSize)
{
	const int keyStringSize=200;

	WCHAR aimpKeys[][keyStringSize]=
	{
		L"AIMP.acs2\\shell\\open\\command",
		L"AIMP.acs3\\shell\\open\\command",
		L"AIMP.flac\\shell\\open\\command",
		L"AIMP.mp3\\shell\\open\\command",
		L"AIMP.aimppl\\shell\\open\\command",
		L"AIMP.m3u\\shell\\open\\command"
	};
	// Calculate size of unspecified dimension
	int aimpKeysCnt=sizeof(aimpKeys)/keyStringSize/sizeof(WCHAR);
	
	WCHAR winampKeys[][keyStringSize]=
	{
		L"Winamp.File\\shell\\Play\\command",
		L"Winamp.LangZip\\shell\\install\\command",
		L"Winamp.PlayList\\shell\\open\\command",
		L"Winamp.SkinZip\\shell\\install\\command"
	};
	int winampKeysCnt=sizeof(winampKeys)/keyStringSize/sizeof(WCHAR);

	WCHAR muiCacheLocations[][keyStringSize]=
	{
		L"Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache",	// win vista/7/8
		L"Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache"									// win 2000/xp/2003
	};
	int muiCacheLocationsCnt=sizeof(muiCacheLocations)/keyStringSize/sizeof(WCHAR);

	HKEY hk_res;

	// trying to find aimp location
	//-----------------------------

	aimpLocation[0]=0;
	for(int i=0; i<aimpKeysCnt; i++)
	{
		if(RegOpenKeyExW(HKEY_CLASSES_ROOT, aimpKeys[i], 0, KEY_READ, &hk_res) == ERROR_SUCCESS)
		{
			// receive string like "D:\exe\AIMP3\AIMP3.exe" "%1"
			if(RegQueryValueExW(hk_res, L"", NULL, NULL, (LPBYTE)aimpLocation, &stringSize) == ERROR_SUCCESS){
				aimpLocation[wcslen(aimpLocation)-5]=0;	// remove "%1" from the end of the string
				TrimString(aimpLocation);		// remove spaces and quotes
			}
			RegCloseKey(hk_res);
			if(IsFileExists(aimpLocation))
				break;	// found!
			else
				aimpLocation[0]=0;
		}
	}
	
	if(wcslen(aimpLocation)==0)
		if(RegOpenKeyExW(HKEY_CLASSES_ROOT, L"CLSID\\{0041494D-5033-4472-6F70-546172676574}\\LocalServer32", 0, KEY_READ, &hk_res) == ERROR_SUCCESS)
		{
			// receive string like D:\exe\AIMP3\AIMP3.exe
			if(RegQueryValueExW(hk_res, L"", NULL, NULL, (LPBYTE)aimpLocation, &stringSize) == ERROR_SUCCESS)
				TrimString(aimpLocation);		// remove spaces and quotes
			RegCloseKey(hk_res);

			if(!IsFileExists(aimpLocation))
				aimpLocation[0]=0;
		}
	
	// search MUI cache
	for(int i=0; i<muiCacheLocationsCnt; i++)
	{
		if(RegOpenKeyExW(HKEY_CURRENT_USER, muiCacheLocations[i], 0, KEY_READ, &hk_res) == ERROR_SUCCESS)
		{
			// searching key with value==AIMP3
			WCHAR keyName[32767/2];
			DWORD keyNameSize;
			WCHAR data[5120];	// 10 kB
			DWORD dataSize;
			DWORD type;
			for(int i=0; true; i++)
			{
				keyNameSize=sizeof(keyName);
				dataSize=sizeof(data);
				if(RegEnumValueW(hk_res, i, keyName, &keyNameSize, NULL, &type, (LPBYTE)data, &dataSize)==ERROR_NO_MORE_ITEMS) break;
				if(type==REG_SZ && wcscmp(data, L"AIMP3")==0)
				{
					wcscpy(aimpLocation, keyName);
					TrimString(aimpLocation);		// remove spaces and quotes
					if(IsFileExists(aimpLocation)) break;	// found!
				}
			}
			RegCloseKey(hk_res);

			if(IsFileExists(aimpLocation))
				break;	// found!
			else
				aimpLocation[0]=0;
		}
	}


	// trying to find winamp location
	//-------------------------------

	// Calculate size of unspecified dimension
	winampLocation[0]=0;
	for(int i=0;i<winampKeysCnt;i++)
	{
		if(RegOpenKeyExW(HKEY_CLASSES_ROOT, winampKeys[i], 0, KEY_READ, &hk_res) == ERROR_SUCCESS)
		{
			// receive string like "D:\exe\AIMP3\AIMP3.exe" "%1"
			if(RegQueryValueExW(hk_res, L"", NULL, NULL, (LPBYTE)winampLocation, &stringSize) == ERROR_SUCCESS){
				winampLocation[wcslen(winampLocation)-5]=0;	// remove "%1" from the end of the string
				TrimString(winampLocation);		// remove spaces and quotes
			}
			RegCloseKey(hk_res);

			if(IsFileExists(winampLocation))
				break;	// found!
			else
				winampLocation[0]=0;
		}
	}
}

// ----------------------------------------------

HWND WaitForWindow(LPWSTR lpszClass, LPWSTR lpszWindow, DWORD timeout)
{
	const DWORD waitInterval=100;	// msec

	HWND hwndRes;
	DWORD startTime=GetTickCount();

	while(GetTickCount()-startTime<timeout){
		hwndRes=FindWindowW(lpszClass, lpszWindow);
		if(hwndRes) break;	// found!
		Sleep(waitInterval);
	}

	return hwndRes;
}

// ----------------------------------------------

void TrimString(LPWSTR str)
{
	int len,start;
	len=lstrlenW(str);
	while(str[0] && (str[len-1]==L' ' || str[len-1]==L'\"')) str[--len]=0;	// remove spaces and quotes from the end of the string
	for(start=0; str[start] && (str[start]==L' ' || str[start]==L'\"'); start++); // skipping spaces and quotes at the start of the string
	MoveMemory(str,str+start,(len-start+1)*sizeof(WCHAR)); // removing starting spaces and quotes
}

// ----------------------------------------------

bool IsFileExists(LPWSTR filename)
{
	WIN32_FIND_DATAW fd;
	HANDLE ffile;

	ffile=FindFirstFileW(filename, &fd);
	FindClose(ffile);

	return ffile!=INVALID_HANDLE_VALUE;
}

// ----------------------------------------------

